//
// GPSMapEdit
// (c) Konstantin Galichsky (kg@geopainting.com), 2002-2006
//
// 'Map properties' dialog, 'Levels' tab.
//

# include "StdAfx.h"
# include "Resource.h"
# include "MapProperties.h"
# include "Map.h"
# include "Types.h"
# include "GarminTypes.h"
# include "NavitelTypes.h"
# include "HoluxTypes.h"
# include "InsertLevel.h"
# include "RemoveLevel.h"
# include "ChangeLevel.h"
# include "PrintUnits.h"
# include "ListView.h"
# include "Globals.h"

static
const char * g_strMSZoom [9] = {
	"under 1.2 km",
	"1.2 - 3 km",
	"3 - 8 km",
	"8 - 12 km",
	"12 - 30 km",
	"50 - 120 km",
	"120 - 300 km",
	"over 300 km",
	"unknown"
};

const size_t c_cMaxBits = 26;
const size_t c_cMaxBits_Garmin = 24;
const size_t c_cMinBits = 8;

static
const char * g_strGPSZoom [c_cMaxBits - c_cMinBits + 1] = {
	"0 m - 30 m",     // 26
	"50 m - 80 m",    // 25
	"120 m",          // 24
	"200 m - 300 m",  // 23
	"500 m",          // 22
	"800 m - 1.2 km", // 21
	"2 km",           // 20
	"3 km",           // 19
	"5 km - 8 km",    // 18
	"12 km",          // 17
	"20 km - 30 km",  // 16
	"50 km",          // 15
	"80 km - 120 km", // 14
	"200 km",         // 13
	"300 - 500 km",   // 12
	"800 km",         // 11
	"1200 km",        // 10
	"unknown",        // 9
	"unknown"         // 8
};

static
void _FillLevels (HWND _hLevels, size_t _cSelItem) {
	CCritSec::CLock lock (g_cs);

	::EnableWindow (_hLevels, ! g_map.Levels.empty ());
	ListView_DeleteAllItems (_hLevels);

	for (size_t cLevel = 0; cLevel < g_map.Levels.size (); ++ cLevel) {
		// Level.
		InsertItem (_hLevels, cLevel, cLevel, "Level%d=", cLevel);

		// Bits.
		const map_level_t & level = g_map.Levels [cLevel];
		if (level.btBits == level.btBitsRangeTo) 
			SetSubItemText (_hLevels, cLevel, 1, "%d", level.btBits);
		else 
			SetSubItemText (_hLevels, cLevel, 1, "%d(%d)", level.btBits, level.btBitsRangeTo);

		// Grid.
		char strGrid [32];
		char strGridUnits [16];
		PrintDistance (strGrid, strGridUnits, g_map.pTypeSet->pfGetLatGrid (g_map, cLevel));
		SetSubItemText (_hLevels, cLevel, 2, "%s %s", strGrid, strGridUnits);

		// GPS zoom.
		const size_t cGPSZoomIdx = c_cMaxBits - level.btBitsRangeTo;
		SetSubItemText (_hLevels, cLevel, 3, "%s", cGPSZoomIdx < sizeof (g_strGPSZoom)/sizeof (g_strGPSZoom [0]) ? g_strGPSZoom [cGPSZoomIdx] : "(INVALID)");

		// MapSource zoom.
		const size_t cZoom = g_map.Levels [cLevel].cZoom;
		SetSubItemText (_hLevels, cLevel, 4, "Zoom%d=%d (%s)", cLevel, cZoom, cZoom < sizeof (g_strMSZoom)/sizeof (g_strMSZoom [0]) ? g_strMSZoom [cZoom] : "(INVALID)");
	}

	// Set the best width.
	AutoSizeColumns (_hLevels);

	// Set selection to the given item.
	SetSelection (_hLevels, _cSelItem);
}

/////////////////////////////////////////////////////////

static
void _UpdateBitsRangeTo (HWND _hWnd, size_t _cLevel) {
	const bool bEnableRange = g_map.pTypeSet == & g_tsNavitel;

	const HWND hBits        = ::GetDlgItem (_hWnd, IDC_LEVEL_BITS);
	const HWND hBitsRangeTo = ::GetDlgItem (_hWnd, IDC_LEVEL_BITS_RANGE_TO);

	const size_t cBitsIdx = ::SendMessage (hBits, CB_GETCURSEL, 0, 0);
	if (cBitsIdx == CB_ERR) {
		::EnableWindow (hBitsRangeTo, FALSE);
		return;
	}
	const BYTE cBits = ::SendMessage (hBits, CB_GETITEMDATA, cBitsIdx, 0);

	::SendMessage  (hBitsRangeTo, CB_RESETCONTENT, 0, 0);
	::EnableWindow (hBitsRangeTo, bEnableRange);
	if (bEnableRange) {
		const BYTE btStartBits = cBits;
		BYTE btEndBits = c_cMinBits;
		if (_cLevel + 1 < g_map.Levels.size ())
			btEndBits = g_map.Levels [_cLevel + 1].btBits + 1;
		if (btEndBits > btStartBits)
			btEndBits = btStartBits;

		for (BYTE bits = btStartBits; bits >= btEndBits; -- bits) {
			assert (c_cMaxBits - bits < sizeof (g_strGPSZoom)/sizeof (g_strGPSZoom [0]));
			if (c_cMaxBits - bits < sizeof (g_strGPSZoom)/sizeof (g_strGPSZoom [0])) {
				char strBuf [64];
				::sprintf (strBuf, "%d (%s)", bits, g_strGPSZoom [c_cMaxBits - bits]);
				
				::SendMessage (hBitsRangeTo, CB_INSERTSTRING, btStartBits - bits, (LPARAM) strBuf);
				::SendMessage (hBitsRangeTo, CB_SETITEMDATA,  btStartBits - bits, bits);
				
				// Set default selection.
				if (bits == btStartBits)
					::SendMessage (hBitsRangeTo, CB_SETCURSEL, 0, 0);
				// Override the default selection, if inside allowed range.
				assert (_cLevel < g_map.Levels.size ());
				if (_cLevel < g_map.Levels.size () && bits == g_map.Levels [_cLevel].btBitsRangeTo)
					::SendMessage (hBitsRangeTo, CB_SETCURSEL, btStartBits - bits, 0);
			}
		}
	} else {
		assert (c_cMaxBits - cBits < sizeof (g_strGPSZoom)/sizeof (g_strGPSZoom [0]));
		if (c_cMaxBits - cBits < sizeof (g_strGPSZoom)/sizeof (g_strGPSZoom [0])) {
			char strBuf [64];
			::sprintf (strBuf, "%d (%s)", cBits, g_strGPSZoom [c_cMaxBits - cBits]);
			
			::SendMessage (hBitsRangeTo, CB_INSERTSTRING, 0, (LPARAM) strBuf);
			::SendMessage (hBitsRangeTo, CB_SETITEMDATA,  0, cBits);
			::SendMessage (hBitsRangeTo, CB_SETCURSEL,    0, 0);
		}		
	}
}

static
INT_PTR CALLBACK _ChangeLevelProc (HWND _hWnd, UINT _uMsg, WPARAM _wParam, LPARAM _lParam) {
	switch (_uMsg) {
		case WM_INITDIALOG: {
			char strBuf [64];

			const size_t cLevel = _lParam;
			::SetWindowLong (_hWnd, GWL_USERDATA, cLevel);

			CCritSec::CLock lock (g_cs);

			// Fill 'Bits' field.
			const HWND hBits = ::GetDlgItem (_hWnd, IDC_LEVEL_BITS);

			const size_t cMaxBits = g_map.pTypeSet == & g_tsGarmin ? c_cMaxBits_Garmin : c_cMaxBits;
			for (size_t cBits = cMaxBits; cBits >= c_cMinBits; -- cBits) {
				assert (c_cMaxBits - cBits < sizeof (g_strGPSZoom)/sizeof (g_strGPSZoom [0]));
				if (c_cMaxBits - cBits < sizeof (g_strGPSZoom)/sizeof (g_strGPSZoom [0])) {
					::sprintf (strBuf, "%d (%s)", cBits, g_strGPSZoom [c_cMaxBits - cBits]);

					::SendMessage (hBits, CB_INSERTSTRING, cMaxBits - cBits, (LPARAM) strBuf);
					::SendMessage (hBits, CB_SETITEMDATA,  cMaxBits - cBits, cBits);

					assert (cLevel < g_map.Levels.size ());
					if (cLevel < g_map.Levels.size () && cBits == g_map.Levels [cLevel].btBits)
						::SendMessage (hBits, CB_SETCURSEL, cMaxBits - cBits, 0);
				}
			}

			// Fill 'Bits range to' field.
			_UpdateBitsRangeTo (_hWnd, cLevel);

			// Fill 'MapSource zoom' field.
			const HWND hZoom = ::GetDlgItem (_hWnd, IDC_LEVEL_ZOOM);

			::EnableWindow (hZoom, g_map.pTypeSet != & g_tsHolux);

			for (size_t cZoom = 0; cZoom < sizeof (g_strMSZoom)/sizeof (g_strMSZoom [0]); ++ cZoom) {
				::sprintf (strBuf, "%d (%s)", cZoom, g_strMSZoom [cZoom]);

				::SendMessage (hZoom, CB_INSERTSTRING, cZoom, (LPARAM) strBuf);
				::SendMessage (hZoom, CB_SETITEMDATA,  cZoom, cZoom);

				assert (cLevel < g_map.Levels.size ());
				if (cZoom == g_map.Levels [cLevel].cZoom)
					::SendMessage (hZoom, CB_SETCURSEL, cZoom, 0);
			}

			return FALSE;
		}

		case WM_COMMAND: {
			switch (LOWORD (_wParam)) {
				case IDC_LEVEL_BITS: {
					if (HIWORD (_wParam) == CBN_SELCHANGE) {
						const size_t cLevel = ::GetWindowLong (_hWnd, GWL_USERDATA);
						_UpdateBitsRangeTo (_hWnd, cLevel);
					}
					return TRUE;
				}

				case IDOK: {
					// Get 'Bits' field.
					const HWND hBits = ::GetDlgItem (_hWnd, IDC_LEVEL_BITS);
					const size_t cBitsIdx = ::SendMessage (hBits, CB_GETCURSEL, 0, 0);
					if (cBitsIdx == CB_ERR)
						break;
					const BYTE cBits = ::SendMessage (hBits, CB_GETITEMDATA, cBitsIdx, 0);

					// Get 'Bits Range To' field.
					const HWND hBitsRangeTo = ::GetDlgItem (_hWnd, IDC_LEVEL_BITS_RANGE_TO);
					const size_t cBitsRangeToIdx = ::SendMessage (hBitsRangeTo, CB_GETCURSEL, 0, 0);
					BYTE cBitsRangeTo = cBits;
					if (cBitsRangeToIdx != CB_ERR) {
						cBitsRangeTo = ::SendMessage (hBitsRangeTo, CB_GETITEMDATA, cBitsRangeToIdx, 0);
						if (cBitsRangeTo > cBits)
							cBitsRangeTo = cBits;
					}

					// Get 'MapSource zoom' field.
					const HWND hZoom = ::GetDlgItem (_hWnd, IDC_LEVEL_ZOOM);
					const size_t cZoomIdx = ::SendMessage (hZoom, CB_GETCURSEL, 0, 0);
					if (cZoomIdx == CB_ERR)
						break;
					const size_t cZoom = ::SendMessage (hZoom, CB_GETITEMDATA, cZoomIdx, 0);

					// Apply.
					const size_t cLevel = ::GetWindowLong (_hWnd, GWL_USERDATA);
					ChangeLevel (cLevel, cBits, cBitsRangeTo, cZoom);
				}
			  
				case IDCANCEL:
					::EndDialog (_hWnd, 0);
					return TRUE;
			}
		}
	}

	return FALSE;
}

/////////////////////////////////////////////////////////

static
size_t _GetLevel (HWND _hWnd) {
	const HWND hLevels = ::GetDlgItem (_hWnd, IDC_MAP_LEVELS);
	const size_t cSel = ListView_GetSelectionMark (hLevels);
	if (cSel == -1)
		return -1;

	return GetLParam (hLevels, cSel);
}

INT_PTR CALLBACK _MapLevelsProc (HWND _hWnd, UINT _uMsg, WPARAM _wParam, LPARAM _lParam) {
	switch (_uMsg) {
		case WM_INITDIALOG: {
			const HWND hLevels = ::GetDlgItem (_hWnd, IDC_MAP_LEVELS);

			ListView_SetExtendedListViewStyleEx (hLevels, LVS_EX_FULLROWSELECT, LVS_EX_FULLROWSELECT);

			AddColumn (hLevels, 0, "Level");
			AddColumn (hLevels, 1, "Bits");
			AddColumn (hLevels, 2, "Lat grid");
			AddColumn (hLevels, 3, "GPS zoom");
			AddColumn (hLevels, 4, "MapSource zoom");

			_FillLevels (hLevels, 0);

			CCritSec::CLock lock (g_cs);
			if (g_map.Levels.empty ()) {
				::EnableWindow (::GetDlgItem (_hWnd, IDC_REMOVE_LEVEL), FALSE);
				::EnableWindow (::GetDlgItem (_hWnd, IDC_CHANGE_LEVEL), FALSE);
			}
			::EnableWindow (::GetDlgItem (_hWnd, IDC_INSERT_LEVEL), g_map.Levels.size () < 10);

			return TRUE;
		}

		case WM_COMMAND: {
			const HWND hLevels = ::GetDlgItem (_hWnd, IDC_MAP_LEVELS);
			const size_t cSel = ListView_GetSelectionMark (hLevels);
			if (cSel == -1) {
				if (LOWORD (_wParam) == IDC_INSERT_LEVEL) {
					InsertLevelBefore (0);
					_FillLevels (hLevels, 0);
				}
				break;
			}

			const size_t cLevel = GetLParam (hLevels, cSel);

			switch (LOWORD (_wParam)) {
				case IDC_INSERT_LEVEL: InsertLevelBefore (cLevel); break;
				case IDC_REMOVE_LEVEL: RemoveLevel       (cLevel); break;

				case IDC_CHANGE_LEVEL:
					DlgBox (IDD_CHANGE_LEVEL, _hWnd, _ChangeLevelProc, cLevel);
					break;
			}

			_FillLevels (hLevels, cSel);

			break;
		}

		case WM_NOTIFY: {
			const NMHDR * const pInfo = reinterpret_cast<const NMHDR *> (_lParam);
			switch (pInfo->code) {
				case NM_RETURN:
				case NM_DBLCLK:
					SendMessage (_hWnd, WM_COMMAND, IDC_CHANGE_LEVEL, 0);
					break;

				case LVN_ITEMCHANGED: {
					const NMLISTVIEW * const pInfo2 = reinterpret_cast<const NMLISTVIEW *> (_lParam);
					if (pInfo2->uNewState & LVIS_SELECTED) {
						CCritSec::CLock lock (g_cs);

						bool bEnableInsert = g_map.Levels.size () < 10;

						bool bEnableRemove = false;
						if (g_map.Levels.size () > 2) {
							const size_t cLevel = _GetLevel (_hWnd);
							if (cLevel == -1)
								bEnableRemove = false;
							else
								bEnableRemove = size_t (pInfo2->lParam) != g_map.Levels.size () - 1;
						}

						bool bEnableChange = ! g_map.Levels.empty ();

						::EnableWindow (::GetDlgItem (_hWnd, IDC_INSERT_LEVEL), bEnableInsert ? TRUE : FALSE);
						::EnableWindow (::GetDlgItem (_hWnd, IDC_REMOVE_LEVEL), bEnableRemove ? TRUE : FALSE);
						::EnableWindow (::GetDlgItem (_hWnd, IDC_CHANGE_LEVEL), bEnableChange ? TRUE : FALSE);
					}

					break;
				}
			}
			break;	
		}
	}

	return FALSE;
}
